﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Pixysoft.Collections
{
    public class CircleQueue<T>
    {
        private readonly ReaderWriterLock locker = new ReaderWriterLock();//

        CircleQueueElement header = null;
        CircleQueueElement footer = null;

        /*
         * check contains the key.
         */
        public bool ContainsKey(string key)
        {
            if (string.IsNullOrEmpty(key))
                return false;

            CircleQueueElement element = header;
            while (element != null)
            {
                if (key.Equals(element.GetElementId(), StringComparison.OrdinalIgnoreCase))
                    return true;
                element = element.Child;
            }
            return false;
        }

        /*
         * put value with key / timestamp to the foot of queue. do not check key duplicate.
         */
        public void Append(string key, T value)
        {
            if (string.IsNullOrEmpty(key))
                throw new VerificationFailedException("missing key / value");

            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                CircleQueueElement element = new CircleQueueElement(key, value);

                if (header == null)
                {
                    header = element;
                    footer = element;
                }
                else
                {
                    footer.Append(element);
                    this.footer = element;
                }
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /*
         * insert value with key/timestamp to the front of queue. do not check key duplicate.
         */
        public void Insert(string key, T value)
        {
            if (string.IsNullOrEmpty(key))
                throw new VerificationFailedException("missing key / value");

            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                CircleQueueElement element = new CircleQueueElement(key, value);

                if (header == null)
                {
                    header = element;
                    footer = element;
                }
                else
                {
                    header.Insert(element);
                    this.header = element;
                }
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /*
         * get value from header and append to the foot of queue.
         */
        public T Poll()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                if (this.header == null)
                    return default(T);

                CircleQueueElement element = this.header;

                this.header = element.Child;

                element.Remove();

                CircleQueueElement rfooter = this.footer;

                this.footer = element;

                if (this.header == null)
                {
                    this.header = element;

                    return element.GetValue();
                }

                rfooter.Append(element);

                return element.GetValue();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        public T PollAndRemove()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                if (this.header == null)
                    return default(T);

                CircleQueueElement element = this.header;

                this.header = element.Child;

                element.Remove();

                if (this.header == null)
                    this.footer = null;

                return element.GetValue();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /*
         * get value from header.
         */
        public T Peek()
        {
            if (this.header == null)
                return default(T);

            string headerKey = this.header.GetElementId();

            return Get(headerKey);
        }

        /*
         * get value by key from queue.
         */
        public T Get(string key)
        {
            if (string.IsNullOrEmpty(key))
                throw new VerificationFailedException(key);


            locker.AcquireReaderLock(Timeout.Infinite);
            try
            {
                if (header == null)
                    return default(T);

                return header.GetValueById(key);
            }
            finally
            {
                locker.ReleaseReaderLock();
            }
        }

        /*
         * remove value from queue by key.
         */
        public T Remove(string key)
        {
            if (string.IsNullOrEmpty(key))
                throw new VerificationFailedException(key);

            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                if (header == null)
                    return default(T);

                CircleQueueElement element = header.GetElementById(key);

                if (element == null)
                    return default(T);

                if (element.IsHeader())
                {
                    header = element.Child;
                }

                if (element.IsFooter())
                {
                    footer = element.Parent;
                }

                element.Remove();

                return element.GetValue();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /*
         * clear all the queue.
         */
        public void ClearAll()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                this.header = null;
                this.footer = null;
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /*
         * get size of the queue.
         */
        public int Size()
        {
            locker.AcquireReaderLock(Timeout.Infinite);

            try
            {
                if (this.header == null)
                    return 0;

                return header.Size();
            }
            finally
            {
                locker.ReleaseReaderLock();
            }
        }

        /*
         * 集合是否为空
         */
        public bool IsEmpty()
        {
            return this.header == null;
        }

        /*
         * to string
         */
        public override string ToString()
        {
            if (header == null)
                return base.ToString();

            return header.ToString();
        }

        private void Adjust()
        {
            if (header != null && !header.IsHeader())
                this.header = this.header.GetHeader();

            if (footer != null && !footer.IsFooter())
                this.footer = this.footer.GetFooter();
        }

        class CircleQueueElement
        {
            private string elementId = null;
            private CircleQueueElement parent = null;
            private CircleQueueElement child = null;
            private T value;

            public CircleQueueElement(string elementId, T value)
            {
                this.elementId = elementId;
                this.value = value;
            }

            /*
             * current element is header
             */
            public bool IsHeader()
            {
                return this.Parent == null;
            }

            /*
             * current element is footer
             */
            public bool IsFooter()
            {
                return this.Child == null;
            }

            /*
             * get parent element.
             */
            public CircleQueueElement Parent
            {
                get
                {
                    return parent;
                }
                set
                {
                    this.parent = value;
                }
            }

            /*
             * get child element.
             */
            public CircleQueueElement Child
            {
                get
                {
                    return child;
                }
                set
                {
                    this.child = value;
                }
            }

            /*
             * set element id
             */
            public string GetElementId()
            {
                return elementId;
            }

            /*
             * get size of queue
             */
            public int Size()
            {
                CircleQueueElement element = GetHeader();
                int counter = 0;
                while (element != null)
                {
                    counter++;
                    element = element.Child;
                }
                return counter;
            }

            /*
             * get value
             */
            public T GetValue()
            {
                return value;
            }

            /*
             * get value by element id.
             */
            public T GetValueById(string elementId)
            {
                CircleQueueElement element = GetElementById(elementId);

                return element == null ? default(T) : element.GetValue();
            }

            /*
             * get element by element id.
             */
            public CircleQueueElement GetElementById(string elementId)
            {
                CircleQueueElement element = GetHeader();

                while (element != null)
                {
                    if (element.elementId.Equals(elementId, StringComparison.OrdinalIgnoreCase))
                        return element;

                    element = element.Child;
                }

                return null;
            }

            /*
             * set value
             */
            public void SetValue(T value)
            {
                this.value = value;
            }

            /*
             * get header, return this if is header
             */
            public CircleQueueElement GetHeader()
            {
                if (parent != null)
                    return parent.GetHeader();

                return this;
            }

            /*
             * get footer, return this if is footer
             */
            public CircleQueueElement GetFooter()
            {
                if (child != null)
                    return child.GetFooter();

                return this;
            }

            /*
             * insert element to the front of current element of link
             */
            public void Insert(CircleQueueElement element)
            {
                // parent - (+element) this - child

                if (this.Parent != null)
                    this.Parent.Child = element;

                element.Parent = this.Parent;

                this.Parent = element;

                element.Child = this;
            }

            /*
             * append element as the child of current element of link
             */
            public void Append(CircleQueueElement element)
            {
                // parent - this (+element) - child

                if (this.Child != null)
                    this.Child.Parent = element;

                element.Child = this.Child;

                element.Parent = this;

                this.Child = element;
            }

            /*
             * remove myself from link
             */
            public void Remove()
            {
                Remove(this);
            }

            /*
             * remove the element from link
             */
            public void Remove(CircleQueueElement element)
            {
                // parent - element - child

                if (element.Parent != null)
                    element.Parent.Child = element.Child;

                if (element.Child != null)
                    element.Child.Parent = element.Parent;

                element.Parent = null;

                element.Child = null;
            }

            /*
             * to string
             */
            public override string ToString()
            {
                StringBuilder buffer = new StringBuilder();
                CircleQueueElement element = this.GetHeader();
                while (element != null)
                {
                    buffer.Append("V:" + element.value + "  ");
                    element = element.Child;
                }
                return buffer.ToString();
            }

        }

    }
}
